package com.afdor.rws.event.support.filter;

import com.afdor.rws.annotation.extension.Extension;
import com.afdor.rws.event.Event;
import com.afdor.rws.event.channel.filter.EventIntercept;
import com.afdor.rws.event.channel.filter.EventInterceptChain;
import com.afdor.rws.event.config.EventConfigUtils;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import org.apache.commons.lang3.BooleanUtils;

import java.util.Arrays;

/**
 * 默认事件拦截链管理
 *
 * @author 悭梵
 * @date Created in 2018-08-20 10:21
 */
@ToString
@EqualsAndHashCode
@Extension(value = "default", order = Extension.NORMAL_ORDER - 100, interfaceClass = EventInterceptChain.class)
public class DefaultEventInterceptChain implements EventInterceptChain, Cloneable {

    public static final int INCREMENT = 5;

    private boolean intercept = false;

    /**
     * 是否副本，没次执行过滤时，采用副本执行
     */
    private boolean copy = false;
    /**
     * 当前EventFilter容量
     */
    private int capacity = 0;
    /**
     * 迭代位置
     */
    private int position = 0;
    /**
     * 默认拦截链为空
     */
    private EventIntercept[] interceptes = new EventIntercept[0];

    @Override
    public boolean isIntercept() {
        return intercept;
    }

    @Override
    public void intercept() {
        intercept = true;
    }

    @Override
    public void doIntercept(@NonNull Event event) {
        if (copy) {
            if (position == capacity || intercept) {
                return;
            }
            interceptes[position++].doIntercept(event, this);
        } else {
            throw new IllegalStateException("Filtering operations can only be performed on replica sets.");
        }
    }

    /**
     * 添加拦截器
     *
     * @param addIntercept
     * @return
     */
    @Override
    public synchronized DefaultEventInterceptChain addIntercept(@NonNull EventIntercept addIntercept) {
        if (!copy) {
            throw new IllegalStateException("This is a replica set, which cannot perform the add operation.");
        }
        for (EventIntercept filter : interceptes) {
            if (filter == addIntercept) {
                return this;
            }
        }

        if (capacity == interceptes.length) {
            EventIntercept[] newInterceptes = new EventIntercept[capacity + INCREMENT];
            System.arraycopy(interceptes, 0, newInterceptes, 0, capacity);
            interceptes = newInterceptes;
        }

        interceptes[capacity++] = addIntercept;

        return this;
    }

    @Override
    public EventInterceptChain copy() {
        if (copy) {
            throw new IllegalStateException("This is a replica set, which cannot perform the generate copy operation.");
        }
        try {
            synchronized (this) {
                return this.clone();
            }
        } catch (CloneNotSupportedException e) {
            throw new IllegalStateException("generate copy exception ", e);
        }
    }

    @Override
    public void destroy() {
        if (copy) {
            interceptes = null;
        } else {
            if (interceptes != null) {
                Arrays.stream(interceptes).forEach(intercept -> intercept.destroy());
                interceptes = null;
            }
        }
    }

    @Override
    protected DefaultEventInterceptChain clone() throws CloneNotSupportedException {
        DefaultEventInterceptChain cloneInterceptChain = (DefaultEventInterceptChain) super.clone();
        // 副本
        cloneInterceptChain.copy = true;
        // 过滤链执行位置下标
        cloneInterceptChain.position = 0;
        // 拦截并终止调用链执行
        cloneInterceptChain.intercept = false;
        // 容量
        cloneInterceptChain.capacity = this.capacity;
        // 拦截器克隆副本
        if (BooleanUtils.toBoolean(EventConfigUtils.getProperty(EventConfigUtils.EVENT_INTERCEPT_CHAIN_CLONE))) {
            EventIntercept[] newInterceptes = new EventIntercept[cloneInterceptChain.capacity];
            System.arraycopy(this.interceptes, 0, newInterceptes, 0, cloneInterceptChain.capacity);
            cloneInterceptChain.interceptes = newInterceptes;
        }
        return cloneInterceptChain;
    }
}
